#ifndef __CLI_PARSE__STRUCTS_H__
#define __CLI_PARSE__STRUCTS_H__

#include "limits.h"

#ifndef HAS_D3D
///
/// Stuff that needs porting ...
///
#define D3DX_PI    ((float)  3.141592654f)
#define D3DXToRadian( degree ) ((degree) *(D3DX_PI / 180.0f))
#endif

#ifdef WIN32
#include <d3d9.h>
#else
#include "../../Epsilon/types.h" // DWORD

typedef DWORD D3DCOLOR;

#define D3DCOLOR_ARGB(a,r,g,b) ((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
#define D3DCOLOR_RGBA(r,g,b,a) D3DCOLOR_ARGB(a,r,g,b)
#define D3DCOLOR_COLORVALUE(r,g,b,a) D3DCOLOR_RGBA((DWORD)((r)*255.f),(DWORD)((g)*255.f),(DWORD)((b)*255.f),(DWORD)((a)*255.f))

typedef struct D3DCOLORVALUE {
  float r;
  float g;
  float b;
  float a;
} D3DCOLORVALUE, *LPD3DCOLORVALUE;

typedef struct D3DMATERIAL9 {
  D3DCOLORVALUE Diffuse;
  D3DCOLORVALUE Ambient;
  D3DCOLORVALUE Specular;
  D3DCOLORVALUE Emissive;
  float Power;
} D3DMATERIAL9, *LPD3DMATERIAL9;
#endif


#ifndef BOOL /// Probably not defined on anything but Windows...
#define BOOL bool
#endif

class eImage;

namespace Underlight
{
  typedef unsigned int id_t;

  // Decrypt text names (for .cli ONLY)
  static void decrypt (void *ptr, const int& size)
  {
  #ifndef GAME_LYR
    unsigned char *src = (unsigned char *)ptr;
    unsigned char *s   = src;
    unsigned char  c   = (src [0] >> 6);

    for (s = src; s < src + size - 1 ; s++) {
      *s <<=2;
      *(s) |= *(s + 1) >> 6;
    }

    *s <<= 2;
    *s |= c;
#endif
  }

  struct FileHeader
  {
    int  version;
    int  num_levels;
    char padding [256];
  };

  struct LevelHeader
  {
    unsigned short level_id;
    unsigned short checksum1;
    unsigned int   file_position;
    char           name [LEVEL_NAME_SIZE];
  };

  struct LevelData
  {
    unsigned long  num_vertices;
    unsigned long  num_sectors;
    unsigned long  num_ornaments;
    unsigned long  num_lines;
    unsigned short num_rooms;
  
    unsigned short checksum2;
  };

  struct VertexData
  {
    signed short x;
    signed short y;
  };

  struct FlatData
  {
    signed short base_height;
    signed short base_line;
    signed short base_sector;
    bool         base_facing;  // True denotes front / back
  };

  struct FileLinedef // 52 bytes
  {
    // vertex numbers
    unsigned short   from;
    unsigned short   to;

    // sector numbers
    unsigned short sector;          // the sector number that this contains
    unsigned short facingsector;    // the sector number that this is facing

    // bitmaps
    short fwall_bitmap;          // fwall is the main wall
    short animation_end;         // the end of the animation cycle for the fwall
    short cwall_bitmap;           // cwall is for the "top slice".

    // offsets
    short fwall_u_offset;   // *** NEW; u = horizontal                      
    short fwall_v_offset;   // *** NEW; v = vertical
    short cwall_u_offset;   // *** NEW
    short cwall_v_offset;   // *** NEW

    int flags;

    // Trip values
    int TripFlags;
    short trip1;           // misc values for different trip wires.
    short trip2;
    short trip3;           // misc values for different trip wires.
    short trip4;

    // for forward compatibility
    short unused1;          // switched bitmap
    short teleport_height;  // *** NEW
    short unused2;          // *** NEW
    short unused3;          // *** NEW
    short unused4;          // *** NEW                      
    short unused5;          // *** NEW
  };

  struct FileSector // 36 bytes
  {
    unsigned short id;

    short ceiling_bitmap;    // in BITMAPINFO array, the top texture.
    short floor_bitmap; // the bottom texture.

    short floor_height;
    short ceiling_height;           

    char floor_slope_angle; // NEW
    unsigned char floor_slope_frac;

    char ceiling_slope_angle; // NEW
    unsigned char ceiling_slope_frac;

    short height_offset; // height offset for actors in this sector

    unsigned int flags;

    short xanim_top; // pixels per frame animation for floor and ceiling
    short yanim_top;
    short xanim_bottom;
    short yanim_bottom;

    short lightlevel;

    short tag; 
    unsigned short room;
  };

  struct Actor // 12 bytes
  {
    int flags; // above flags
    short x;   // x,y coords
    short y;
    short angle; 
    short finfo; 
  };

  struct FileRoom // 44 bytes
  {
    unsigned short id;
    unsigned short unknown;
    char  name [ROOM_NAME_SIZE];
    short background;
    short background_palette; // *** NEW
    unsigned int flags;
  };

  //
  // Resource Structures
  //
  struct ResourceHeader
  {
    unsigned long file_position;
  };

  //
  // Texture / Flat Structures
  //
  struct TextureFileHeader
  {
    int num_textures;
    int num_flats;
  };

  struct TextureHeader : ResourceHeader
  {
    unsigned short  height;
    unsigned short  width;
    unsigned short  id;
    unsigned short  palette;
  };

  struct FlatHeader : ResourceHeader
  {
    unsigned short  height;
    unsigned short  width;
    unsigned short  id;
    unsigned short  palette;
  };

  //
  // Effects Structures
  //
  struct EffectFileHeader
  {
    int             num_palettes;
    int             num_visual_effects;
    int             num_sound_effects;
  };

  struct PaletteHeader : ResourceHeader
  {
    unsigned short  count;
    unsigned short  id;
  };

  struct VisualEffectHeader : ResourceHeader
  {
    unsigned short  height;
    unsigned short  width;
    unsigned char   views;
    unsigned char   frames;
    unsigned short  count;
    unsigned short  bpp;
    unsigned short  id;
    short           palette;
    short           rle; 
    int             size;
  };

  struct SoundEffectHeader : ResourceHeader
  {
    unsigned long   format_length;
    unsigned long   data_length;
    unsigned short  count;
    unsigned short  id;
  };
}

// Modified Underlight structs
namespace Underlight
{
  class Linedef
  {
  public:
    // vertex numbers
    unsigned short   from;
    unsigned short   to;

    //Vertex vtx_from;
    //Vertex vtx_to;
  //private:

    // bitmaps
    short fwall_bitmap;          // fwall is the main wall
    short animation_end;         // the end of the animation cycle for the fwall
    short cwall_bitmap;           // cwall is for the "top slice".
  };
}

struct BITMAPINFO_4DX
{
  eImage*       image;
  eImage*       normal_map;
  unsigned char unused;
  short         palette;
  short         w,h;
}; // 8 bytes

//#pragma pack ()


// A quicker alternative to casting, to convert floats to ints.
// Note: the values returned are rounded not clipped. i.e 4.5 -> 5, not 4
static inline int float2int(double double_val)
{
	long * long_ptr = (long *)&double_val;
	long long_val;

	double_val += 6755399441055744.0;// add the "magic number" to align the bits
	long_val = *long_ptr;// read the long integer value from double_val's bits

	return long_val;
}

struct level_file_header_t
{
  int  version;
  int  num_levels;
  char padding [256];
};

struct level_header_t
{
  unsigned short level_id;
  unsigned short checksum1;
  unsigned int   file_position;
  char           name [LEVEL_NAME_SIZE];
};


class PickingData {
public:
  PickingData (unsigned char room_, bool flat, short surface_)
  {
    room     = room_;
    //sign_bit = (flat != 0);
    if (flat) {
      surface = surface_;
    } else {
      surface = surface_ + MAX_SECTORS;
    }
    reserve  = 0xcc; // For memory debugging
  };

  PickingData (int data)
  {
    memcpy (&_x, &data, sizeof (_x));
  }

  void print (void);

  operator int (void) const {
    return _x;
  }

  /// Rewrite with platform agnostic struct!
  D3DCOLORVALUE rgba (void)
  {
    float r_, g_, b_, a_;
      
    r_ = (float)(r / 255.0f);
    g_ = (float)(g / 255.0f);
    b_ = (float)(b / 255.0f);
    a_ = (float)(a / 255.0f);

    D3DCOLORVALUE out;
    out.r = r_;
    out.g = g_;
    out.b = b_;
    out.a = 1.0f;

    return out;
  }
    
  /// @TODO: Need to fool with some more, subject to alignment
  union {
    struct {
      signed short  surface;//  : 16; ///< Sector / Line ID
      unsigned char room;//     : 8;  ///< Room ID
      unsigned char reserve;//  : 8;  ///< Reserved for future use (may be unusable???)
    };

    struct {
      unsigned int r : 8;
      unsigned int g : 8;
      unsigned int b : 8;
      unsigned int a : 8;
    };

    int _x;
  };
};

class e3dVector2
{
public:
  e3dVector2 (void) { };
  e3dVector2 (float _x, float _y) : x (_x),
                                    y (_y) { };

  float x, y;
};

class e3dBoundingBox
{
public:
  e3dBoundingBox (void) {
    max [0] = max [1] = -9999999.0f;
    min [0] = min [1] =  9999999.0f;
  }

  e3dVector2 Center (void)
  {
    e3dVector2 ret;

    ret.x = (max [0] + min [0]) / 2.0f;
    ret.y = (max [1] + min [1]) / 2.0f;

    return ret;
  }

  bool PointInBounds (e3dVector2& p)
  {
    if ((p.x <= max [0] && p.x >= min [0]) &&
        (p.y <= max [1] && p.y >= min [1]))
      return true;

    return false;
  }

  void AddPoint (float x, float y) {
    if (x > max [0])
      max [0] = x;

    if (x < min [0])
      min [0] = x;

    if (y > max [1])
      max [1] = y;

    if (y < min [1])
      min [1] = y;
  }

  float max [2];
  float min [2];
};


struct ul_wall_heights_t
{
  struct {
    float to;
    float from;
  } top;

  struct {
    float to;
    float from;
  } bottom;
};

struct ul_tex_coord_surf_t
{
  float v;
};

struct ul_tex_coords_t
{
  ul_tex_coords_t (void) {
    from.u       = 0.0f;
    from.floor.v = 0.0f;
    from.ceil.v  = 0.0f;

    to.u         = 0.0f;
    to.floor.v   = 0.0f;
    to.ceil.v    = 0.0f;
  }

  struct from_t {
    float u;

    ul_tex_coord_surf_t floor;
    ul_tex_coord_surf_t ceil;

    void flip_u (void) {
      u = -u;
    }

    void flip_v (void) {
      floor.v = -floor.v;
      ceil.v  = -ceil.v;
    }

    void swap_v (void) {
      float fSwap = floor.v;
      floor.v     = ceil.v;
      ceil.v      = fSwap;
    }
  } from;

  struct to_t {
    float u;

    ul_tex_coord_surf_t floor;
    ul_tex_coord_surf_t ceil;

    void flip_u (void) {
      u = -u;
    }

    void flip_v (void) {
      floor.v = -floor.v;
      ceil.v  = -ceil.v;
    }

    void swap_v (void) {
      float fSwap = floor.v;
      floor.v     = ceil.v;
      ceil.v      = fSwap;
    }
  } to;

  void flip_u (void) {
    from.flip_u ();
    to.flip_u   ();
  }

  void swap_u (void) {
    float fSwap = from.u;
    from.u = to.u;
    to.u   = fSwap;
  }

  void swap_v (void) {
    from.swap_v ();
    to.swap_v   ();
  }

  void flip_v (void) {
    from.flip_v ();
    to.flip_v   ();
  }
};

#endif /* __CLI_PARSE__STRUCTS_H__ */
